Skip to content

crypto: support loading private keys through STORE loaders#63949

Open
panva wants to merge 1 commit into
nodejs:mainfrom
panva:keyobject-stores
Open

crypto: support loading private keys through STORE loaders#63949
panva wants to merge 1 commit into
nodejs:mainfrom
panva:keyobject-stores

Conversation

@panva

@panva panva commented Jun 16, 2026

Copy link
Copy Markdown
Member

Allow crypto.createPrivateKey() and APIs that consume private keys, such as crypto.sign(), crypto.privateDecrypt(), crypto.diffieHellman(), and crypto.decapsulate(), to load private keys through configured OpenSSL STORE loaders using WHATWG URL objects.

This supports direct URL input:

import { createPrivateKey } from 'node:crypto';

const key = createPrivateKey(
  new URL('file:///path/to/private-key.pem'),
);

It also supports object input with an optional passphrase or PIN and OpenSSL property query:

import { createPrivateKey, sign } from 'node:crypto';

const key = createPrivateKey({
  key: new URL('pkcs11:token=node;object=signing-key;type=private'),
  passphrase: process.env.PKCS11_PIN,
  properties: 'provider=pkcs11',
});

const signature = sign('sha256', Buffer.from('payload'), key);

The provider implementing the STORE loader must already be loaded in Node.js' default OpenSSL library context, for example through OpenSSL configuration. This API does not load providers.

passphrase is forwarded to the loader as its passphrase or PIN. properties is an OpenSSL property query used to select the STORE loader; it is not appended to the URL and is distinct from provider-specific URI parameters.

Why URL?

A URL distinguishes an external key reference from existing key material. Strings, buffers, typed arrays, and formatted objects retain their existing PEM, DER, JWK, and raw-key semantics. This avoids adding STORE loading to the key import/export format namespace.

URL input is accepted only in private-key contexts. A public key can be derived by first loading the private key and passing the resulting KeyObject to crypto.createPublicKey().

The resulting provider-backed key is an ordinary KeyObject and interoperates with existing private-key operations, key metadata, public-key derivation, and toCryptoKey() where supported by the key algorithm and provider. Provider policy continues to control permitted operations and private-key exportability.

The native STORE-loading path is available with OpenSSL >= 3.

Permission model

STORE loading adds the global crypto.store permission, enabled with --allow-crypto-store and exposed through the runtime permission APIs:

process.permission.has('crypto.store'); // true
process.permission.drop('crypto.store');
process.permission.has('crypto.store'); // false

This permission grants broad authority to configured STORE loaders. Loaders may access files, devices, tokens, or the network, and that access is not constrained by the fs.read, fs.write, or net permission scopes.

This broad capability matches OpenSSL's behavior: STORE loaders perform their own I/O, and OpenSSL may try its file loader before another loader for opaque URIs.

When STORE access is denied, Node.js redacts the URI from the permission error resource and permission diagnostics.

Passphrases and errors

Credentials should be supplied through passphrase rather than embedded in the URI.

If OpenSSL requests a passphrase and none was provided, Node.js throws ERR_MISSING_PASSPHRASE, matching existing encrypted private-key import behavior. Incorrect passphrases and provider failures continue to surface as OpenSSL errors.

Node.js redacts URIs from its own permission-denial errors. Errors reported by OpenSSL or a provider after loading begins may still include the URI.

@panva panva added the wip Issues and PRs that are still a work in progress. label Jun 16, 2026
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/actions
  • @nodejs/config
  • @nodejs/crypto
  • @nodejs/gyp
  • @nodejs/security-wg

@nodejs-github-bot nodejs-github-bot added lib / src Issues and PRs related to general changes in the lib or src directory. needs-ci PRs that need a full CI run. labels Jun 16, 2026
@panva panva requested a review from tniessen June 17, 2026 09:04
@panva panva force-pushed the keyobject-stores branch 2 times, most recently from 43f0d74 to 96c5081 Compare June 18, 2026 08:52
@panva panva removed the wip Issues and PRs that are still a work in progress. label Jun 18, 2026
@panva panva force-pushed the keyobject-stores branch 2 times, most recently from ddd7eaf to 35bb771 Compare June 18, 2026 09:03
jasnell
jasnell previously approved these changes Jun 19, 2026
@panva panva force-pushed the keyobject-stores branch from 35bb771 to 9d23b6b Compare June 29, 2026 13:01
@panva panva dismissed jasnell’s stale review June 29, 2026 13:02

I changed the feature shape quite a bit. Dismissing a stale review.

@panva panva force-pushed the keyobject-stores branch from f32c6b7 to dffa62d Compare June 29, 2026 16:13
@panva panva force-pushed the keyobject-stores branch from 6f3d2cb to e496577 Compare July 8, 2026 22:27
@jasnell jasnell added the semver-minor PRs that contain new features and should be released in the next minor version. label Jul 8, 2026
@panva

This comment was marked as resolved.

@panva panva added the wip Issues and PRs that are still a work in progress. label Jul 8, 2026
@codecov

codecov Bot commented Jul 9, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 63.14199% with 122 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.22%. Comparing base (db7cc5b) to head (70d8d79).
⚠️ Report is 10 commits behind head on main.

Files with missing lines Patch % Lines
src/crypto/crypto_keys.cc 60.00% 20 Missing and 12 partials ⚠️
src/crypto/crypto_sig.cc 62.66% 11 Missing and 17 partials ⚠️
src/crypto/crypto_rsa.cc 42.50% 15 Missing and 8 partials ⚠️
src/crypto/crypto_dsa.cc 25.92% 17 Missing and 3 partials ⚠️
src/crypto/crypto_ec.cc 6.66% 11 Missing and 3 partials ⚠️
lib/internal/crypto/keys.js 94.36% 4 Missing ⚠️
src/permission/crypto_store_permission.cc 87.50% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #63949      +/-   ##
==========================================
- Coverage   90.25%   90.22%   -0.04%     
==========================================
  Files         741      743       +2     
  Lines      241192   241603     +411     
  Branches    45421    45537     +116     
==========================================
+ Hits       217691   217975     +284     
- Misses      15085    15154      +69     
- Partials     8416     8474      +58     
Files with missing lines Coverage Δ
lib/internal/process/permission.js 81.70% <100.00%> (+0.22%) ⬆️
lib/internal/process/pre_execution.js 97.94% <100.00%> (+<0.01%) ⬆️
src/crypto/crypto_cipher.cc 76.73% <100.00%> (-0.15%) ⬇️
src/crypto/crypto_keys.h 65.11% <ø> (ø)
src/env.cc 85.50% <100.00%> (+0.04%) ⬆️
src/node_options.cc 76.74% <100.00%> (+0.02%) ⬆️
src/node_options.h 98.02% <100.00%> (+0.50%) ⬆️
src/permission/crypto_store_permission.h 100.00% <100.00%> (ø)
src/permission/permission.cc 82.56% <100.00%> (+0.24%) ⬆️
src/permission/permission.h 100.00% <ø> (ø)
... and 7 more

... and 31 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@panva panva added review wanted PRs that need reviews. and removed wip Issues and PRs that are still a work in progress. labels Jul 9, 2026
@nodejs-github-bot

This comment was marked as outdated.

@nodejs-github-bot

This comment was marked as outdated.

@panva

This comment was marked as resolved.

Accept WHATWG URL objects in private-key inputs and load referenced keys
through OpenSSL STORE loaders. Pass optional property queries and
passphrases while preserving provider-owned EVP_PKEY objects for
ordinary KeyObject and CryptoKey operations.

Signed-off-by: Filip Skokan <panva.ip@gmail.com>
@panva panva force-pushed the keyobject-stores branch from b1d5d84 to 70d8d79 Compare July 10, 2026 19:43
@panva panva changed the title crypto: support OpenSSL STORE private keys crypto: support loading private keys through STORE loaders Jul 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

experimental Issues and PRs related to experimental features. lib / src Issues and PRs related to general changes in the lib or src directory. needs-ci PRs that need a full CI run. review wanted PRs that need reviews. semver-minor PRs that contain new features and should be released in the next minor version.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants